package framework.beans.support;

import framework.beans.config.InchBeanDefinition;
import util.OSCheckAndConvert;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * the reader class to read from configuration files
 */
public class InchBeanDefinitionReader {

    private List<String> registryBeanClasses = new ArrayList<String>();

    private Properties config = new Properties();

    // the key that defined which package need to be scanned
    // eg:  this is the line in the configuration file: "scanPackage=demo.actions"
    private final String SCAN_PACKAGE = "scanPackage";

    /**
     * constructor
     * @param locations the location of the files
     */
    public InchBeanDefinitionReader(String[] locations) {
        //loop each file
        for (String location : locations) {
            InputStream is = this.getClass().getClassLoader().getResourceAsStream(location.replace("classpath:",""));
            try {
                config.load(is);
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            //scan the file
            doScanner(config.getProperty(SCAN_PACKAGE));
        }
    }

    /**
     * Scan the file
     * @param scanPackage the raw address of the package
     */
    private void doScanner(String scanPackage) {
        //get url
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.","/"));
        //get file path
        File classPath = new File(OSCheckAndConvert.stringConvert(url.getFile()));
        //loop each file
        for (File file : classPath.listFiles()) {
            //check directory
            if (file.isDirectory()) {
                doScanner(scanPackage + "." + file.getName());
            } else {
                //check if the file is a class file
                if(!file.getName().endsWith(".class")) {continue;}
                String className = scanPackage + "." + file.getName().replace(".class","");
                //record class that need to be mapped
                registryBeanClasses.add(className);
            }
        }
    }


    /**
     * create a list of beanDefinitions by names in the registryBenClass
     * @return
     */
    public List<InchBeanDefinition> loadBeanDefinitions() {
        List<InchBeanDefinition> result = new ArrayList<InchBeanDefinition>();
        try {
            //loop all classes need to be mapped
            for(String className : registryBeanClasses) {
                Class<?> beanClass = Class.forName(className);
                //skip interface class , interface class was added with its implement class
                if(beanClass.isInterface()) {continue;}
                //add to bean definitions list
                result.add(doCreateBeanDefinition(beanClass.getName(),beanClass.getName(),false,false));
                //add interfaces
                Class<?>[] interfaces = beanClass.getInterfaces();
                for(Class<?> interfaceClass : interfaces) {
                    // add interface and its implement class to beanDefinitions
                    result.add(doCreateBeanDefinition(interfaceClass.getName(),beanClass.getName(),false,false));
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * create bean definitions
     * @param factoryBeanName the simple name of the class or the interface class name of this class eg:DemoAction
     * @param beanClassName the full name of this class eg: demo.actions.DemoAction
     * @return
     */
    private InchBeanDefinition doCreateBeanDefinition(String factoryBeanName, String beanClassName,
                                                      boolean lazyInit, boolean singleton) {
        InchBeanDefinition beanDefinition = new InchBeanDefinition();
        beanDefinition.setBeanClassName(beanClassName);
        beanDefinition.setFactoryBeanName(factoryBeanName);
        beanDefinition.setSingleton(singleton);
        return beanDefinition;
    }

    /**
     * @return configurations properties
     */
    public Properties getConfig() {
        return this.config;
    }
}
